/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chatsystem;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;

import protocol.Message;
import protocol.Port;


/**
 *
 * @author marine
 */
public class ChatNI implements Runnable {

    private Controller c;
    private ObjectOutputStream objectOS;
    private ObjectInputStream objectIS;
    private ByteArrayInputStream byteIS;
    private ByteArrayOutputStream byteOS;
    private DatagramSocket sockReception;
    private DatagramSocket sockEmission;
    private boolean continuer = true;

    /**
     * Crée un nouveau ChatNI
     * @param c le controller
     * @throws IOException 
     */
    public ChatNI(Controller c) throws IOException {
        this.c = c;
        this.init();
    }

    /**
     * Initialisation des sockets
     * @throws IOException 
     */
    private void init() throws IOException {

        sockReception = new DatagramSocket(Port.receptionUDP);
        sockEmission = new DatagramSocket(Port.emissionUDP);

        /* Pour éviter de rester bloqué plus de 100ms dans le receive
         * Permet de terminer proprement le thread
         */
        sockReception.setSoTimeout(500);
        
        /* Si on s'était précédemment déconnecté, continuer est à false
         * On le passe à true
         */
        this.continuer = true;
    }

    /**
     * Envoi d'un message
     * @param m le message à envoyer
     * @param address l'adresse IP de destination
     * @throws IOException 
     */
    public void send(Message m, InetAddress address) throws IOException {
        /* Si on s'était précédemment déconnecté, les sockets sont fermés
         * Il faut donc les rouvrir
         */
        if (sockEmission.isClosed()) {
            init();
        }
        
        byteOS = new ByteArrayOutputStream();
        objectOS = new ObjectOutputStream(byteOS);


        //on serialise notre objet
        objectOS.writeObject(m);
        objectOS.flush();


        DatagramPacket dp = new DatagramPacket(
                byteOS.toByteArray(), byteOS.size(),address, Port.receptionUDP);
        sockEmission.send(dp);        
        System.out.println("On a envoyé ça : "+m+ " à "+address);

    }

    /**
     * Réception d'un message
     * Méthode bloquante à la base, 
     * plus vraiment grâce à un timeout mis en place dans init()
     * @throws IOException
     * @throws ClassNotFoundException 
     */
    public void receive() throws IOException, ClassNotFoundException {

        //return inStream.readObject();
        byte[] messreceive = new byte[2560];

         DatagramPacket dp = new DatagramPacket(
               messreceive , messreceive.length);
        sockReception.receive(dp);

        byteIS = new ByteArrayInputStream(messreceive);
        objectIS = new ObjectInputStream(byteIS);

        this.c.traiterMessage((Message) objectIS.readObject(),dp.getAddress());
    }

    /**
     * Dédiée à la réception des messages
     */
    public void run() {

        while (continuer) {
            try {
                this.receive();   
            } catch (SocketTimeoutException timEx) {
                /* Si on est là c'est à cause du timeout du socket de réception
                 * Ce n'est pas un problème, on ne fait rien de spécial
                 */
            } catch (IOException ex) {
                Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(ChatNI.class.getName()).log(Level.SEVERE, null, ex);
            }
            
        }
        System.out.println("On sort proprement du thread\n");
        closeAll();
    }
    
    /**
     * Met continuer à false
     * Ce qui permet de sortir du while et terminer le thread proprement
     */
    public void fermeture() {
        this.continuer = false;  
    }
    
    /**
     * Ferme les streams et les sockets
     */
    private void closeAll() {
        try {
            this.byteIS.close();
            this.byteOS.close();
        } catch (IOException ex) {
            System.out.println("Erreur de fermeture des Streams"+ex);
        }
        this.sockEmission.close();
        this.sockReception.close();
    }
}
